﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Phone.Controls;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
using STEM123.StemService;

namespace STEM123
{
    /// <summary>
    /// View a presentation page.
    /// </summary>
    public partial class ViewPresentation : PhoneApplicationPage
    {
        #region Private members

        /// <summary>
        /// Reference to view model to be used.
        /// </summary>
        private IViewModel viewModel;

        /// <summary>
        /// Media element used to display video.
        /// </summary>
        private MediaElement presentationMediaElement;

        /// <summary>
        /// Adds a question to grid.
        /// </summary>
        /// <param name="grid">Grid reference.</param>
        /// <param name="question">Question text.</param>
        private void addQuestion(Grid grid, string question)
        {
            TextBlock textBlockQuestion = new TextBlock();
            textBlockQuestion.Text = question;
            textBlockQuestion.TextWrapping = TextWrapping.Wrap;
            textBlockQuestion.VerticalAlignment = System.Windows.VerticalAlignment.Top;
            textBlockQuestion.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
            textBlockQuestion.FontSize = 24;
            textBlockQuestion.FontWeight = FontWeights.Bold;

            Grid.SetRow(textBlockQuestion, 0);
            Grid.SetColumn(textBlockQuestion, 0);

            grid.Children.Add(textBlockQuestion);
        }

        /// <summary>
        /// Adds an image to grid.
        /// </summary>
        /// <param name="grid">Grid reference.</param>
        /// <param name="imageSource">Uri of imgae.</param>
        /// <param name="mediaType">Type of media used in slide.</param>
        private void addImageAnswer(Grid grid, Uri imageSource, MediaTypes mediaType)
        {
            Border borderAnswer = new Border();
            borderAnswer.Height = 360;
            borderAnswer.Width = 360;
            borderAnswer.BorderBrush = new SolidColorBrush(Colors.Gray);
            borderAnswer.BorderThickness = new Thickness(1.0);
            
            Image imageAnswer = new Image();
            imageAnswer.Height = 350;
            imageAnswer.Width = 350;
            imageAnswer.Stretch = Stretch.Fill;
            imageAnswer.Margin = new Thickness(2, 2, 0, 0);
            
            if (viewModel is ExplainViewModel)
            {
                // display locally stored image
                imageAnswer.Source = ExplainViewModel.Instance.ReadImage(imageSource);
            }
            else
            {
                // display locally stored temporary image 
                imageAnswer.Source = new BitmapImage(new Uri("LoadingContent.png", UriKind.Relative));

                // download and display image
                Dispatcher.BeginInvoke(() =>
                {
                    downloadImage(imageAnswer, imageSource);
                });
            }

            Grid.SetRow(imageAnswer, 1);
            Grid.SetColumn(imageAnswer, 0);

            Grid.SetRow(borderAnswer, 1);
            Grid.SetColumn(borderAnswer, 0);

            grid.Children.Add(imageAnswer);
            grid.Children.Add(borderAnswer);
        }

        /// <summary>
        /// Adds video to grid.
        /// </summary>
        /// <param name="grid">Grid reference.</param>
        /// <param name="mediaSource">Uri of video.</param>
        /// <param name="mediaType">Type of media used in slide.</param>
        /// <param name="localSource">True if locally stored media to be used else false.</param>
        private void addVideoAnswer(Grid grid, Uri mediaSource, MediaTypes mediaType, bool localSource)
        {
            TextBlock txtblockStatus = new TextBlock();
            txtblockStatus.VerticalAlignment = VerticalAlignment.Bottom;
            txtblockStatus.HorizontalAlignment = HorizontalAlignment.Center;
            txtblockStatus.TextWrapping = TextWrapping.Wrap;
            txtblockStatus.FontSize = 20;
            txtblockStatus.Height = 30;
            txtblockStatus.Text = "Loading";

            Border borderAnswer = new Border();
            borderAnswer.Height = 360;
            borderAnswer.Width = 360;
            borderAnswer.BorderBrush = new SolidColorBrush(Colors.Gray);
            borderAnswer.BorderThickness = new Thickness(1.0);

            MediaElement mediaAnswer = new MediaElement();
            mediaAnswer.Height = 350;
            mediaAnswer.Width = 350;
            mediaAnswer.Stretch = Stretch.Fill;
            mediaAnswer.AutoPlay = true;
            mediaAnswer.Margin = new Thickness(2, 2, 0, 0);

            RotateTransform transform = new RotateTransform();
            transform.CenterX =  transform.CenterY = 175;
            transform.Angle = 90.0;
            mediaAnswer.RenderTransform = transform;

            mediaAnswer.CurrentStateChanged += (cs, ce) =>
            {
                Dispatcher.BeginInvoke(() =>
                {
                    MediaElement videoElement = (MediaElement)cs;
                    txtblockStatus.Text = videoElement.CurrentState.ToString();
                });
            };

            mediaAnswer.Tap += (ts, te) =>
            {
                MediaElement videoElement = (MediaElement)ts;

                if (videoElement.CurrentState == MediaElementState.Paused || videoElement.CurrentState == MediaElementState.Stopped)
                {
                    videoElement.Play();
                }
                else if (videoElement.CurrentState == MediaElementState.Playing)
                {
                    videoElement.Pause();
                }
            };

            Grid.SetRow(borderAnswer, 1);
            Grid.SetColumn(borderAnswer, 0);

            Grid.SetRow(mediaAnswer, 1);
            Grid.SetColumn(mediaAnswer, 0);

            Grid.SetRow(txtblockStatus, 3);
            Grid.SetColumn(txtblockStatus, 0);

            grid.Children.Add(borderAnswer);
            grid.Children.Add(mediaAnswer);
            grid.Children.Add(txtblockStatus);

            this.presentationMediaElement = mediaAnswer;

            if (localSource)
            {
                mediaAnswer.SetSource(ExplainViewModel.Instance.ReadVideo(mediaSource));
            }
            else
            {
                mediaAnswer.Source = mediaSource;
            }
        }

        /// <summary>
        /// Adds audio to grid.
        /// </summary>
        /// <param name="grid">Grid reference.</param>
        /// <param name="mediaSource">Uri of audio.</param>
        /// <param name="mediaType">Type of media used in slide.</param>
        /// <param name="localSource">True if local source to be used else false.</param>
        private void addAudioAnswer(Grid grid, Uri mediaSource, MediaTypes mediaType, bool localSource)
        {
            Button btnAudio = new Button();
            btnAudio.VerticalAlignment = VerticalAlignment.Center;
            btnAudio.HorizontalAlignment = HorizontalAlignment.Center;
            btnAudio.FontSize = 20;
            btnAudio.Height = 96;
            btnAudio.Width = 96;

            ImageBrush playPauseBrush = new ImageBrush();
            playPauseBrush.ImageSource = new BitmapImage(new Uri("Images/speaker.png", UriKind.Relative));
            btnAudio.Background = playPauseBrush;

            btnAudio.Click += (ts, te) =>
            {
                try
                {
                    ((Button)ts).IsEnabled = false;

                    if (MediaPlayer.State != MediaState.Playing)
                    {
                        if (localSource)
                        {
                            Thread playthread = new Thread(() =>
                            {
                                MemoryStream stream = ExplainViewModel.Instance.ReadAudio(mediaSource);
                                SoundEffect sound = new SoundEffect(stream.ToArray(), Microphone.Default.SampleRate, AudioChannels.Mono);
                                sound.Play();
                            });

                            playthread.Start();
                        }
                        else
                        {
                            Song songAnswer = Song.FromUri(viewModel.Term, mediaSource);
                            FrameworkDispatcher.Update();
                            MediaPlayer.Play(songAnswer);
                        }
                    }
                }
                catch (InvalidOperationException exception)
                {
                    Console.WriteLine(exception);
                }
                finally
                {
                    ((Button)ts).IsEnabled = true;
                }
            };

            if (mediaType == MediaTypes.AudioOnly)
            {
                Grid.SetRow(btnAudio, 1);
            }
            else if (mediaType == MediaTypes.AudioWithImage)
            {
                Grid.SetRow(btnAudio, 2);
            }
            else
            {
                throw new NotSupportedException("Unsupported audio media type combination: " + mediaType.ToString());
            }

            Grid.SetColumn(btnAudio, 0);

            grid.Children.Add(btnAudio);
        }

        /// <summary>
        /// Adds text answer to grid.
        /// </summary>
        /// <param name="grid">Grid reference.</param>
        /// <param name="answer">Answer text.</param>
        /// <param name="mediaType">Type of media used in slide.</param>
        private void addTextAnswer(Grid grid, string answer, MediaTypes mediaType)
        {
            TextBlock textblockAnswer = new TextBlock();
            textblockAnswer.Text = answer;
            textblockAnswer.VerticalAlignment = VerticalAlignment.Top;
            textblockAnswer.HorizontalAlignment = HorizontalAlignment.Left;
            textblockAnswer.TextWrapping = TextWrapping.Wrap;
            textblockAnswer.FontSize = 24;
            textblockAnswer.Width = 400; // no need to set height, since entire content is wrapped inside scrollviewer

            if (mediaType == MediaTypes.TextOnly)
            {
                Grid.SetRow(textblockAnswer, 1);
            }
            else if (mediaType == MediaTypes.TextWithImage)
            {
                Grid.SetRow(textblockAnswer, 2);
            }

            Grid.SetColumn(textblockAnswer, 0);

            grid.Children.Add(textblockAnswer);
        }

        /// <summary>
        /// Downloads and displays an image.
        /// </summary>
        /// <param name="imageControl">Image control reference.</param>
        /// <param name="imageSource">Uri of image.</param>
        private void downloadImage(Image imageControl, Uri imageSource)
        {
            WebClient client = new WebClient();

            client.OpenReadCompleted += ((s, e) =>
            {
                if (e.Error == null && !e.Cancelled)
                {
                    BitmapImage image = new BitmapImage();
                    image.SetSource(e.Result);
                    imageControl.Source = image;
                }
            });

            client.OpenReadAsync(imageSource);
        }

        #endregion

        #region Protected members

        /// <summary>
        /// Occurs when page is navigated to, from some other page.
        /// </summary>
        /// <param name="e">Navigation event arguments.</param>
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            // determine selected view
            IDictionary<string, string> parameters = this.NavigationContext.QueryString;
            ViewModels selectedView = (ViewModels)Enum.Parse(typeof(ViewModels), parameters["view"].ToString(), true);

            // get reference of current view model
            if (selectedView == ViewModels.Explain)
            {
                this.viewModel = ExplainViewModel.Instance;
            }
            else if (selectedView == ViewModels.Search)
            {
                this.viewModel = SearchViewModel.Instance;
            }
            else
            {
                throw new ArgumentException("Unsupported view.");
            }

            // read state and prepare UI
            this.PanSlides.Title = this.viewModel.Term;

            Slide[] slides = this.viewModel.Slides;

            for (int i = 0; i < 3; i++)
            {
                LocalSlide localSlide = slides[i] as LocalSlide;

                // process panaroma item only if corresponding question exists
                if (!string.IsNullOrEmpty(slides[i].Question))
                {
                    Grid grid = (Grid)this.FindName("Grid" + i);

                    addQuestion(grid, slides[i].Question);

                    if (slides[i].ImageSource != null || (localSlide != null && localSlide.LocalImageSource != null))
                    {
                        Uri imageUri = slides[i].ImageSource;
                        if (this.viewModel is ExplainViewModel)
                        {
                            imageUri = ((LocalSlide)slides[i]).LocalImageSource;
                        }

                        if (imageUri != null)
                        {
                            addImageAnswer(grid, imageUri, slides[i].MediaType);
                        }
                    }

                    if (!string.IsNullOrEmpty(slides[i].Answer))
                    {
                        addTextAnswer(grid, slides[i].Answer, slides[i].MediaType);
                    }

                    if (slides[i].MediaSource != null || (localSlide != null && localSlide.LocalMediaSource != null))
                    {
                        Uri mediaUri = slides[i].MediaSource;
                        bool localSource = false;

                        if (this.viewModel is ExplainViewModel)
                        {
                            mediaUri = ((LocalSlide)slides[i]).LocalMediaSource;
                            localSource = true;
                        }

                        if (mediaUri != null)
                        {
                            if (slides[i].MediaType == MediaTypes.Video)
                            {
                                addVideoAnswer(grid, mediaUri, slides[i].MediaType, localSource);
                            }
                            else if (slides[i].MediaType == MediaTypes.AudioOnly || slides[i].MediaType == MediaTypes.AudioWithImage)
                            {
                                addAudioAnswer(grid, mediaUri, slides[i].MediaType, localSource);
                            }
                        }
                    }
                }
                else
                {
                    // remove panaroma item, if corresponding question does not exist
                    ((PanoramaItem)this.FindName("Pan" + i)).Visibility = System.Windows.Visibility.Collapsed;
                }
            }
        }

        /// <summary>
        /// Occurs when page is navigated from, to other page.
        /// </summary>
        /// <param name="e">Event specific arguments.</param>
        protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);

            MediaPlayer.Stop();
        }

        #endregion

        #region Public members

        /// <summary>
        /// Initializes a new instance of ViewPresentation.
        /// </summary>
        public ViewPresentation()
        {
            InitializeComponent();
        }

        #endregion
    }
}